package br.com.music;

import br.com.music.player.Metronome;
import br.com.music.player.Metronome.MetronomeType;
import java.io.IOException;

import br.com.music.scale.Mode;
import br.com.music.scale.ModeType;
import br.com.music.player.Player;
import br.com.music.rhythm.TimeSignature;
import br.com.music.utils.Logger;
import java.io.File;
import java.util.Vector;
import org.jfugue.Rhythm;

public class Factory {

    static final int DEF_TEST  = 0;

    /**
     * Generate random music with 30 notes.
     *
     * @author felipperag
     */
    static boolean test0() {
        Mode mode = new Mode(Pitch.LA, ModeType.LOCRIAN);
        Player p = new Player();
        try {
            //p.play(mode, 4, Instrument.ACOUSTIC_BASS);
            //p.save("/opt/teste.mid");
            p.randMusic(mode, 30, 4);
        } catch(Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * Test all musical modes.
     *
     * @author savio.sena
     */
    static boolean test1() {
        Vector<Mode> modes = new Vector<Mode>();

        modes.add(new Mode(Pitch.DO, ModeType.IONIAN));
        modes.add(new Mode(Pitch.DO, ModeType.DORIAN));
        modes.add(new Mode(Pitch.DO, ModeType.PHRYGIAN));
        modes.add(new Mode(Pitch.DO, ModeType.LYDIAN));
        modes.add(new Mode(Pitch.DO, ModeType.MIXOLYDIAN));
        modes.add(new Mode(Pitch.DO, ModeType.AEOLIAN));
        modes.add(new Mode(Pitch.DO, ModeType.LOCRIAN));

        Player p = new Player();

        for(Mode mode : modes) {
            System.out.println("Playing: " + mode.getDescription());
            p.play(mode); 
        }
        return true;
    }

    /**
     * Load a Text file as JFugue Pattern and play it.
     *
     * @note This is not really a MusicLabs test since it uses only JFugue
     * elements; but it's still useful since we have to compare results
     * from time to time.
     * 
     * @param path Path to text file containing JFugue language music.
     * @return True if the test succeed; false otherwise.
     */
    static boolean test2(String path) {
        try {
            File midiFile = new File(path);
            org.jfugue.Player p = new org.jfugue.Player();
            org.jfugue.Pattern pat = org.jfugue.Pattern.loadPattern(midiFile);
            Logger.debug(pat.toString());
            p.play(pat);
        } catch(Exception e) {
            Logger.exception(e.getMessage(), e);
            return false;
        }
        return true;
    }

    static boolean test3() {
        try {
            org.jfugue.Rhythm rhy = new Rhythm();
            rhy.setLayer(1, "O..oO...O..oOO..");
            rhy.setLayer(2, "..*...*...*...*.");
            rhy.addSubstitution('*', "[ACOUSTIC_SNARE]i");
            rhy.addSubstitution('O', "[BASS_DRUM]i");
            rhy.addSubstitution('o', "Rs [BASS_DRUM]s");
            rhy.addSubstitution('.', "Ri");
            org.jfugue.Pattern pat = rhy.getPattern();
            pat.repeat(4);
            org.jfugue.Player p = new org.jfugue.Player();
            p.play(pat);
        } catch(Exception e) {
            Logger.exception(e.getMessage(), e);
            return false;
        }
        return true;
    }

    /**
     * Metronome test suite.
     *
     * @return This method returns true whenever all test cases passes.
     */
    static boolean test4() {
        try {
            TimeSignature sig;
            Metronome metronome = new Metronome(MetronomeType.RHYTHMIC);
            Player p = new Player();

            // binary semibreve
            //Logger.speak("Binary semibreve");
            sig = new TimeSignature(TimeSignature.BINARY, Duration.SEMIBREVE);
            metronome.setTimeSignature(sig);
            //Logger.debug(metronome.toString());
            p.play(metronome);

            // binary semiquaver
            //Logger.speak("Binary semiquaver");
            sig = new TimeSignature(TimeSignature.BINARY, Duration.SEMIQUAVER);
            metronome.setTimeSignature(sig);
            //Logger.debug(metronome.toString());
            p.play(metronome);

            // ternary semibreve
            //Logger.speak("Ternary semibreve");
            sig = new TimeSignature(TimeSignature.TERNARY, Duration.SEMIBREVE);
            metronome.setTimeSignature(sig);
            Logger.debug(metronome.toString());
            p.play(metronome);
                                   
            // quaternary semibreve
            //Logger.speak("Quaternary semibreve");
            sig = new TimeSignature(TimeSignature.QUATERNARY, Duration.SEMIBREVE);
            metronome.setTimeSignature(sig);
            Logger.debug(metronome.toString());
            p.play(metronome);

        } catch (Exception ex) {
            Logger.exception(ex.getMessage(), ex);
        }
        return true;
    }

    /**
     * Invoke a test case.
     * 
     * @param id Test case ID.
     * @return Returns true if and only if the test passes, and false for failure.
     */
    static boolean runTest(String[] args, int testID) {
        boolean r = false;
        try {
            // TODO use reflection?
            switch(testID) {
                case 0: r = test0(); break;
                case 1: r = test1(); break;
                case 2: r = test2(args[1]); break;
                case 3: r = test3(); break;
                case 4: r = test4(); break;
                default: assert false;
            }
        } catch(Exception e) {
            Logger.debug("Test-"+testID +" failed! : " + e.getMessage());
            e.printStackTrace();
        }
        return r;
    }

    /**
     * Program entrypoint.
     * 
     * @param args Not used.
     * @throws java.io.IOException
     */
    public static void main(String[] args) throws IOException {

        int testID = (args == null) ? DEF_TEST : new Integer(args[0]);

        boolean testResult = runTest(args, testID);
        if(testResult) {
            Logger.debug("[!] Test "+ testID +" passed.");
        } else {
            Logger.debug("[X] Test "+ testID +" failed.");
        }
    }

}
